//阻塞队列的实现
#pragma once

#include<iostream>
#include<string>
#include<queue>
#include<pthread.h>
#include<ctime>

const int defaultcap = 5;      //for test

template<typename T>
class BlockQueue
{
private:
    bool IsFull() { return _q.size() >= _cap; }      //这是判断阻塞队列是否是满的
    bool IsEmpty() { return _q.empty(); }

public:
    BlockQueue(int cap = defaultcap)
        :_cap(cap),_csleep_num(0),_psleep_num(0)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_full_cond,nullptr);
        pthread_cond_init(&_empty_cond,nullptr);
    }

//入队列
    void Equeue(const T &in)
    {
        //加锁
        pthread_mutex_lock(&_mutex);
        //生产者调用
        
        while(IsFull())    //利用while的循环本身的判断能力，增加代码的健壮性
        {
            //等待   应该让生产者线程进行等待

    //重点1：pthread_cond_wait调用成功，挂起当前线程之前，要先自动释放锁！
    //重点2：当线程被唤醒的时候，默认就在临界区内唤醒！要从 pthread_cond_wait
    //  成功返回，需要当前线程重新申请_mutex锁
    //重点3：如果我被唤醒，但是申请锁失败，那么我就会在锁上阻塞等待！！！
            _psleep_num++;      //判断满了  休眠个数就++
            
/*  
    问题1：pthread_cond_wait  是函数么？是函数调用，有可能失败！休眠失败就会pthread_cond_wait立即返回，但是当前队列是
          满的,就会导致往队列多插数据！
    问题2：pthread_cond_wait 可能会因为条件其实不满足，pthread_cond_wait 伪唤醒
*/
    pthread_cond_wait(&_full_cond,&_mutex);
            _psleep_num--;
        }

        //100%确定：队列有空间
        _q.push(in);

        //临时方案
        if(_csleep_num > 0)     //消费者大于0
            pthread_cond_signal(&_empty_cond);     //唤醒消费者     
        std::cout<<"唤醒消费者..."<<std::endl;

        
    //放在锁前，不会在条件变量等了，而是在锁上等，放锁前可以！锁一旦释放就持有了锁
        // pthread_cond_signal(&_empty_cond);    //唤醒放在锁前还是锁后？前面能唤醒，但是申请锁一定失败，因为锁还没解和释放
        //解锁
        pthread_mutex_unlock(&_mutex);     //TODO
    //放在锁后唤醒，也可以
        // pthread_cond_signal(&_empty_cond);             
        
/////////////// 这是因为都要持有锁，锁让他们固定只有一个 /////////////////
/////////////// 使用条件变量的时候，不能使用if，应该使用while！！！ /////////////////

    }

    T Pop()
    {
        //消费者调用
        pthread_mutex_lock(&_mutex);
        //先判定  是否是空的
    while(IsEmpty())
        {
            _csleep_num++;
            //空的就需要等待，在条件变量等待
            pthread_cond_wait(&_empty_cond,&_mutex);   //消费者要把锁腾出来
            _csleep_num--;
        }
        T data = _q.front();
        _q.pop();

        if(_psleep_num > 0)
        {
            pthread_cond_signal(&_full_cond);     //唤醒生产者        
            std::cout<<"唤醒消费者"<<std::endl;   
        }     
        // pthread_cond_signal(&_full_cond);          
        pthread_mutex_unlock(&_mutex);
        return data;
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_full_cond);
        pthread_cond_destroy(&_empty_cond);
    }

private:
    std::queue<T> _q;     //临界资源
    int _cap;       //容量大小

    pthread_mutex_t _mutex;     //一把锁， 保护临界资源
    pthread_cond_t _full_cond;    //满了对应的条件变量
    pthread_cond_t _empty_cond;   //空了对应的条件变量

    int _csleep_num;     //消费者休眠的个数
    int _psleep_num;     //生产者休眠的个数

};



